package skills;

import loadsave.Memento;
import loadsave.loadsaveobject.Iterator;
import loadsave.loadsaveobject.LoadSaveObject;
import loadsave.loadsaveobject.LoadSaveObjectComposite;
import loadsave.loadsaveobject.LoadSavePrimitive;
import scheduler.DuplicateEventException;
import scheduler.Notifiable;
import scheduler.Scheduler;
import handlers.CEMvtHandler;
import influence_radii.InfluenceRadius;
import effects.CombatEffect;
import effects.Effect;
import entity.Entity;
import factory.EffectFactory;
import factory.InfluenceRadiusFactory;


/**
 * The purpose of ActiveSkill is to provide a concrete
 * subtype of Skill.  ActiveSkills include attacking
 * (i.e., one-handed, two-handed, brawling, staff, etc�)
 * and spells (i.e., fireball, sleep, heal, etc�).
 * This class is necessary to have a concrete
 * implementation of the basic skills.
 * 
 * The Skill class is responsible for implementing the
 * interface for execute(), which is responsible for creating
 * an interaction handler and the combat effects to propagate,
 * as well as scheduling the move() command on to the
 * handler.  Propagations are derived from the
 * influenceRadius.
 * 
 * @author DangMH
 *
 */
public class ActiveSkill extends Skill {
	private CEMvtHandler CEMH;
	private Effect effect;
	private InfluenceRadius influenceRadius;
	private int minRange;
	private int propagationDelay;
	private int rangeDx;
	private int rangeMod;
		
	/**
	 * Empty constructor
	 */
	public ActiveSkill(String modelName) {
		this( modelName, 1, null, null, 0, 0, 0, 1, 0 );
	}
	
	/**
	 * Parameterized constructor
	 */
	public ActiveSkill( String modelName, int level, Effect effect, InfluenceRadius influenceRadius, int minRange, int propogationDelay, int mp) {
		super(modelName, level, mp);
		this.effect = effect;
		this.influenceRadius = influenceRadius;
		this.minRange = minRange;
		this.propagationDelay = propogationDelay;
	}
	
	/**
	 * Parameterized constructor
	 */
	public ActiveSkill( String modelName, int level, Effect effect, InfluenceRadius influenceRadius, int minRange, int propogationDelay, int rangeDx, int rangeMod , int mp) {
		super(modelName, level, mp);
		this.effect = effect;
		this.influenceRadius = influenceRadius;
		this.minRange = minRange;
		this.propagationDelay = propogationDelay;
		this.rangeDx = rangeDx;
		this.rangeMod = rangeMod;
	}
	
	public int getMaxRange() {
		//System.out.printf( "level = %d\nrangeMod = %d\nrangeDx = %d\nminRange = %d\n", level, rangeMod, rangeDx, minRange );
		return ( level / rangeMod * rangeDx + minRange );
	}

	public ActiveSkill() { }

	/**
	 * Responsible for creating an interaction handler
	 * and the combat effects to propagate, as well as
	 * scheduling the move() command on to the handler.
	 * Propagations are derived from the influenceRadius.
	 * @throws DuplicateEventException 
	 */
	public void execute( Entity e ) {
		influenceRadius.setDirection( e.getDirection() );
		CEMH = new CEMvtHandler(
			new CombatEffect(
				getLevel(),
				effect,
				minRange,
				getMaxRange(),
				e.getStats(),
				influenceRadius
			),
			e
		);
		
		try {
			Scheduler.getInstance().schedule(
				new Notifiable(){
					private int currRange = 0;
					
					public void execute() {
						CEMH.clean();
						
						CEMH.propagate();
						
						if( currRange++ > ( getMaxRange() - minRange ) ) {
							//System.out.printf( "currRange: %d\nmaxRange: %d\nminRange: %d\n", currRange, getMaxRange(), minRange );
							CEMH.clean();
							Scheduler.getInstance().cancel( this );
						}
					}
				},
				propagationDelay
			);
		} catch (DuplicateEventException dee) {
			dee.printStackTrace();
		}
	}

	public InfluenceRadius getInfluenceRadius() {
		return influenceRadius;
	}
	
	public Memento getMemento() {
		return new Memento() {
			
			public LoadSaveObject getState() {
				LoadSaveObjectComposite ret = new LoadSaveObjectComposite( "", "ActiveSkill" );
				Iterator<LoadSaveObject> iter = ActiveSkill.super.getMemento().getState().getIterator();
				for( iter.init(); iter.hasNext(); iter.next() ) {
					ret.add( iter.getCurrKey().getValue(), iter.getCurrVal());
				}
				
				LoadSaveObject e = effect.getMemento().getState();
				e.setName( "effect" );
				ret.add(e.getName(), e);

				LoadSaveObject ir = influenceRadius.getMemento().getState();
				ir.setName( "influenceRadius" );
				ret.add( ir.getName(), ir );
				
				ret.add( "minRange", new LoadSavePrimitive( "minRange", "int", String.valueOf( minRange ) ) );
				ret.add( "propagationDelay", new LoadSavePrimitive( "propagationDelay", "int", String.valueOf( propagationDelay ) ) );
				ret.add("rangeDx", new LoadSavePrimitive("rangeDx", "int", String.valueOf(rangeDx)));
				ret.add("rangeMod", new LoadSavePrimitive("rangeMod", "int", String.valueOf(rangeMod)));
				
				return ret;
			}

			public void restoreState(LoadSaveObject lso) {
				ActiveSkill.super.getMemento().restoreState( lso );
				
				effect = EffectFactory.createEffect( lso.get("effect") );
				influenceRadius = InfluenceRadiusFactory.createInfluenceRadius( lso.get( "influenceRadius" ).getType() );
				minRange = Integer.valueOf( lso.get("minRange").getValue() );
				propagationDelay = Integer.valueOf( lso.get("propagationDelay").getValue() );
				rangeDx = Integer.valueOf(lso.get("rangeDx").getValue());
				rangeMod = Integer.valueOf(lso.get("rangeMod").getValue());
			}
		};
	}
	
	public ActiveSkill clone() {
		ActiveSkill ret = new ActiveSkill(getName(), level, effect.clone(), influenceRadius.clone(), minRange, propagationDelay, rangeDx, rangeMod, super.getMPCost());
		return ret;
	}
}
